package com.wiscamp.ninechapters.problems.domain.services;

import com.wiscamp.ninechapters.common.core.DateTimeHelper;
import com.wiscamp.ninechapters.common.core.JsonHelper;
import com.wiscamp.ninechapters.common.exception.ExceptionHandler;
import com.wiscamp.ninechapters.common.exception.ServiceException;
import com.wiscamp.ninechapters.problems.domain.events.SheetReviewedEvent;
import com.wiscamp.ninechapters.problems.domain.models.exams.ScoringRule;
import com.wiscamp.ninechapters.problems.domain.models.problems.answers.Answer;
import com.wiscamp.ninechapters.problems.domain.models.problems.answers.AnswerValidator;
import com.wiscamp.ninechapters.problems.domain.models.problems.problems.Problem;
import com.wiscamp.ninechapters.problems.domain.models.problems.questions.*;
import com.wiscamp.ninechapters.problems.domain.models.sheets.*;
import com.wiscamp.ninechapters.problems.domain.repositories.ExamRepository;
import com.wiscamp.ninechapters.problems.domain.repositories.ProblemCollectionRepository;
import com.wiscamp.ninechapters.problems.domain.repositories.ProblemRepository;
import com.wiscamp.ninechapters.problems.domain.repositories.SheetRepository;
import com.wiscamp.ninechapters.problems.infrastructure.repositories.ProblemBuilder;
import com.wiscamp.ninechapters.problems.math.LatexInequalitySolution;
import cube.common.core.CollectionUtils;
import cube.common.core.ParseUtils;
import cube.ddd.aspect.HandleServiceException;
import cube.ddd.domain.BaseDomainService;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.Collection;
import java.util.HashMap;
import java.util.Objects;

/**
 * 答题卡领域服务
 */
@Service
public class SheetService extends BaseDomainService {

    private final SheetRepository sheetRepository;

    private final ProblemRepository problemRepository;

    private final ProblemCollectionRepository problemCollectionRepository;

    private final ExamRepository examRepository;

    private final ProblemBuilder problemBuilder;


    public SheetService(SheetRepository sheetRepository, ProblemRepository problemRepository, ProblemCollectionRepository problemCollectionRepository, ExamRepository examRepository, ProblemBuilder problemBuilder) {
        this.sheetRepository = sheetRepository;
        this.problemRepository = problemRepository;
        this.problemCollectionRepository = problemCollectionRepository;
        this.problemBuilder = problemBuilder;
        this.examRepository = examRepository;
    }


    /**
     * 用户提交答题卡
     *
     * @param sheet
     * @return
     */
    @HandleServiceException
    @Transactional(transactionManager = "problemsTransactionManager")
    public Sheet submitSheet(@NotNull Sheet sheet) {
        var foundSheet = sheetRepository.getSheetByTask(sheet.getTaskId());
        if (Objects.nonNull(foundSheet)) {
            // 如果此用户的此任务已提交过答题卡，则直接返回数据库中的答题卡
            logger.warn("此练习任务已提交过答题卡了。任务编号为： {}.", sheet.getTaskId());
            return foundSheet;
        }

        // 持久化答题卡,存入数据库
        Sheet newSheet = persistenceSheet(sheet);
        if (Objects.isNull(newSheet)) {
            logger.error("持久化保存答题卡失败。任务编号: {}", sheet.getTaskId());
            return null;
        }

        // 批改答题卡，因为答题卡获取后需立即获取答题报告，所以无法通过异步方式实现
        var success = reviewSheet(newSheet);
        if (!success)
            logger.warn("批改答题卡失败。答题卡编号: {}", newSheet.getSheetId());
        // 返回批改后的答题卡
        return sheetRepository.getSheet(newSheet.getSheetId());
    }

    /**
     * 批改答题卡客观题
     *
     * @param sheet
     * @return
     */
    private boolean reviewSheet(@NotNull Sheet sheet) {
        try {
            double totalScore;
            // 对答题卡评分
            totalScore = scoreSheet(sheet);

            // 更新答题卡客观题批改后的总分
            updateSheetScore(sheet, totalScore);

            // 更新练习任务状态, 更新任务状态
            applicationContext.publishEvent(new SheetReviewedEvent(this, sheet.getTaskId(), sheet.getSheetId()));
            return true;
        } catch (Exception ex) {
            logger.error("Failed to correct the sheet.", ex);
            return false;
        }
    }

    /**
     * 对答题卡明细进行评分
     *
     * @param sheet
     * @return
     */
    private double scoreSheet(@NotNull Sheet sheet) {
        try {
            // region deleted code
            /*double totalScore = 0;
            for (var detail : sheet.getDetails()) {
                double score = 0.0;
                if (detail instanceof TrueFalseSheetDetail) {
                    TrueFalseQuestion question = ((TrueFalseSheetDetail) detail).getQuestion();
                    score = scoreTrueFalse((TrueFalseSheetDetail) detail, question);
                } else if (detail instanceof MultipleChoiceSheetDetail) {
                    MultipleChoiceQuestion question = ((MultipleChoiceSheetDetail) detail).getQuestion();
                    score = scoreMultipleChoice((MultipleChoiceSheetDetail) detail, question);
                } else if (detail instanceof MultipleSelectionSheetDetail) {
                    MultipleSelectionQuestion question = ((MultipleSelectionSheetDetail) detail).getQuestion();
                    score = scoreMultipleSelection((MultipleSelectionSheetDetail) detail, question);
                } else if (detail instanceof CalculationSheetDetail) {
                    CalculationQuestion question = ((CalculationSheetDetail) detail).getQuestion();
                    score = scoreCalculation((CalculationSheetDetail) detail, question);
                } else if (detail instanceof CompletionSheetDetail) {
                    CompletionQuestion question = ((CompletionSheetDetail) detail).getQuestion();
                    score = scoreCompletion((CompletionSheetDetail) detail, question);
                } else if (detail instanceof GeometryCompletionSheetDetail) {
                    GeometryCompletionQuestion question = ((GeometryCompletionSheetDetail) detail).getQuestion();
                    score = scoreGeometryCompletion((GeometryCompletionSheetDetail) detail, question);
                } else if (detail instanceof ColumnCalculationSheetDetail) {
                    ColumnCalculationQuestion question = ((ColumnCalculationSheetDetail) detail).getQuestion();
                    score = scoreColumnCalculation((ColumnCalculationSheetDetail) detail, question);
                } else if (detail instanceof StatisticsCalculationSheetDetail) {
                    StatisticsCalculationQuestion question = ((StatisticsCalculationSheetDetail) detail).getQuestion();
                    score = scoreStatisticsCalculation((StatisticsCalculationSheetDetail) detail, question);
                } else if (detail instanceof ProbabilityCalculationSheetDetail) {
                    ProbabilityCalculationQuestion question = ((ProbabilityCalculationSheetDetail) detail).getQuestion();
                    score = scoreProbabilityCalculation((ProbabilityCalculationSheetDetail) detail, question);
                } else if (detail instanceof GeometryCalculationSheetDetail) {
                    GeometryCalculationQuestion question = ((GeometryCalculationSheetDetail) detail).getQuestion();
                    score = scoreGeometryCalculation((GeometryCalculationSheetDetail) detail, question);
                } else if (detail instanceof WordSheetDetail) {
                    WordQuestion question = ((WordSheetDetail) detail).getQuestion();
                    score = scoreWord((WordSheetDetail) detail, question);
                } else if (detail instanceof ProveSheetDetail) {
                    ProveQuestion question = ((ProveSheetDetail) detail).getQuestion();
                    // score = scoreProve((WordSheetDetail) detail, question);
                    score = 0.0;
                } else if (detail instanceof UnitConversionSheetDetail) {
                    UnitConversionQuestion question = ((UnitConversionSheetDetail) detail).getQuestion();
                    score = scoreUnitConversion((UnitConversionSheetDetail) detail, question);
                } else if (detail instanceof RatioProportionSheetDetail) {
                    RatioProportionQuestion question = ((RatioProportionSheetDetail) detail).getQuestion();
                    score = scoreSheetDetailWithAnswer(detail, question);
                } else if (detail instanceof FactorsAndMultiplesSheetDetail) {
                    FactorsMultiplesQuestion question = ((FactorsAndMultiplesSheetDetail) detail).getQuestion();
                    score = scoreSheetDetailWithAnswer(detail, question);
                } else if (detail instanceof EquationInequalitySheetDetail) {
                    EquationInequalityQuestion question = ((EquationInequalitySheetDetail) detail).getQuestion();
                    score = scoreEquationInequalityAnswer((EquationInequalitySheetDetail) detail, question);
                } else {
                    logger.error("未找到匹配的答题卡明细评分方法。{} / {}", detail.getDetailId(), detail.getQuestionId());
                }

                totalScore = totalScore + score;
                // TODO: 计算成长值和更新知识星图掌握度
            }
            return totalScore;*/
            // endregion

            double totalScore = 0;
            for (var detail : sheet.getDetails()) {
                double score = 0.0;
                if (detail instanceof TrueFalseSheetDetail) {
                    TrueFalseQuestion question = ((TrueFalseSheetDetail) detail).getQuestion();
                    score = detail.scoreAnswers(question);
                } else if (detail instanceof MultipleChoiceSheetDetail) {
                    MultipleChoiceQuestion question = ((MultipleChoiceSheetDetail) detail).getQuestion();
                    score = detail.scoreAnswers(question);
                } else if (detail instanceof MultipleSelectionSheetDetail) {
                    MultipleSelectionQuestion question = ((MultipleSelectionSheetDetail) detail).getQuestion();
                    score = detail.scoreAnswers(question);
                } else if (detail instanceof CalculationSheetDetail) {
                    CalculationQuestion question = ((CalculationSheetDetail) detail).getQuestion();
                    score = detail.scoreAnswers(question);
                } else if (detail instanceof CompletionSheetDetail) {
                    CompletionQuestion question = ((CompletionSheetDetail) detail).getQuestion();
                    score = detail.scoreAnswers(question);
                } else if (detail instanceof GeometryCompletionSheetDetail) {
                    GeometryCompletionQuestion question = ((GeometryCompletionSheetDetail) detail).getQuestion();
                    score = detail.scoreAnswers(question);
                } else if (detail instanceof ColumnCalculationSheetDetail) {
                    ColumnCalculationQuestion question = ((ColumnCalculationSheetDetail) detail).getQuestion();
                    score = detail.scoreAnswers(question);
                } else if (detail instanceof StatisticsCalculationSheetDetail) {
                    StatisticsCalculationQuestion question = ((StatisticsCalculationSheetDetail) detail).getQuestion();
                    score = detail.scoreAnswers(question);
                } else if (detail instanceof ProbabilityCalculationSheetDetail) {
                    ProbabilityCalculationQuestion question = ((ProbabilityCalculationSheetDetail) detail).getQuestion();
                    score = detail.scoreAnswers(question);
                } else if (detail instanceof GeometryCalculationSheetDetail) {
                    GeometryCalculationQuestion question = ((GeometryCalculationSheetDetail) detail).getQuestion();
                    score = detail.scoreAnswers(question);
                } else if (detail instanceof WordSheetDetail) {
                    WordQuestion question = ((WordSheetDetail) detail).getQuestion();
                    score = detail.scoreAnswers(question);
                } else if (detail instanceof ProveSheetDetail) {
                    ProveQuestion question = ((ProveSheetDetail) detail).getQuestion();
                    // score = scoreProve((WordSheetDetail) detail, question);
                    score = 0.0;
                } else if (detail instanceof UnitConversionSheetDetail) {
                    UnitConversionQuestion question = ((UnitConversionSheetDetail) detail).getQuestion();
                    score = detail.scoreAnswers(question);
                } else if (detail instanceof RatioProportionSheetDetail) {
                    RatioProportionQuestion question = ((RatioProportionSheetDetail) detail).getQuestion();
                    score = detail.scoreAnswers(question);
                } else if (detail instanceof FactorsAndMultiplesSheetDetail) {
                    FactorsMultiplesQuestion question = ((FactorsAndMultiplesSheetDetail) detail).getQuestion();
                    score = detail.scoreAnswers(question);
                } else if (detail instanceof EquationInequalitySheetDetail) {
                    EquationInequalityQuestion question = ((EquationInequalitySheetDetail) detail).getQuestion();
                    score = detail.scoreAnswers(question);
                } else {
                    logger.error("未找到匹配的答题卡明细评分方法。{} / {}", detail.getDetailId(), detail.getQuestionId());
                }

                totalScore = totalScore + score;
            }
            return totalScore;
        } catch (Exception ex) {
            logger.error("Failed to score the sheet. The sheet id is {}", sheet.getSheetId(), ex);
            if (ex instanceof ServiceException)
                throw ex;
            ExceptionHandler.throwServiceException(ex, logger, ExceptionHandler.UNKNOWN_SERVICE_EXCEPTION_CODE, sheet);
        }
        return 0.0;
    }

    /**
     * 持久化答题卡业务实体
     *
     * @param sheet
     * @return
     */
    private Sheet persistenceSheet(@NotNull Sheet sheet) {
        try {
            return sheetRepository.saveSheet(sheet);
        } catch (Exception ex) {
            logger.error("Failed to persistence the sheet.", ex);
            var sheetJson = JsonHelper.toJson(sheet);
            logger.error("SHEET JSON: {}", sheetJson);
        }
        return null;
    }

    private void updateSheetScore(@NotNull Sheet sheet, double score) {
        try {
            sheet.setGainScore(score);
            sheetRepository.saveSheet(sheet);
        } catch (Exception ex) {
            logger.error("Failed to update score of the sheet.", ex);
        }
    }

    // region score question
    private int getCorrectStatus(int answerQty, int correctQty) {
        // 全错误
        if (correctQty == 0) return 0;
        // 全正确
        if (correctQty == answerQty) return 1;
        // 部分正确
        return 2;
    }


    private Answer matchAnswerByEquationInequality(Collection<Answer> answers, String sheetAnswerText) {
        var extensionText = sheetAnswerText.trim().replace(" ", "");
        if (extensionText.contains("_")) {
            var compareOperator = LatexInequalitySolution.getCompareOperator(extensionText);
            int leftIndex = extensionText.indexOf("_");
            int rightIndex = extensionText.indexOf(compareOperator);
            extensionText = extensionText.substring(0, leftIndex) + extensionText.substring(rightIndex);
        }
        for (var answer : answers) {
            var answerText = answer.getAnswerText().trim().replace(" ", "");
            var compareOperator = LatexInequalitySolution.getCompareOperator(answerText);
            if (answerText.contains("_")) {
                int leftIndex = answerText.indexOf("_");
                int rightIndex = answerText.indexOf(compareOperator);
                answerText = answerText.substring(0, leftIndex) + answerText.substring(rightIndex);
            }
            if (answerText.equalsIgnoreCase(extensionText))
                return answer;
        }
        return null;
    }

    private double scoreEquationInequalityAnswer(EquationInequalitySheetDetail detail, EquationInequalityQuestion question) {
        var sheetAnswers = detail.getSheetAnswers();
        var answers = question.getAnswers();
        int correctQty = 0;
        boolean allCorrect;
        HashMap<Integer, Answer> matchedAnswers = new HashMap<>();
        for (var extension : sheetAnswers) {
            //logger.debug("正在校验解方程答题卡明细TextExtension {} - {}", extension.getSheetDetailId(), extension.getContentHypertext());
            String sheetAnswerText = extension.getContentText();
            Answer correctAnswer = matchAnswerByEquationInequality(answers, sheetAnswerText);

            if (Objects.nonNull(correctAnswer)) {
                if (Objects.isNull(matchedAnswers.get(correctAnswer.getAnswerIndex()))) {
                    // 此正确答案还没有被匹配过
                    matchedAnswers.put(correctAnswer.getAnswerIndex(), correctAnswer);
                } else {
                    // 此正确答案还已被匹配过
                    correctAnswer = null;
                }
            }

            boolean isCorrect = false;
            if (Objects.isNull(correctAnswer)) {
                logger.error("Cannot find the correct answer. The sheet detail extension is {}.", extension.getContentText());
            } else {
                isCorrect = true;
                correctQty = correctQty + 1;
            }
            scoreSheetAnswers(extension, correctAnswer, isCorrect);
        }

        var correctStatus = getCorrectStatus(answers.size(), correctQty);
        double totalScore = getQuestionTotalScore(question);
        double gainScore = totalScore * ((double) correctQty / answers.size());
        return reviewSheetDetail(detail, correctStatus, gainScore);
    }

    private double scoreWord(WordSheetDetail detail, WordQuestion question) {
        var sheetAnswers = detail.getSheetAnswers();
        var answers = question.getAnswers();
        int correctQty = 0;
        boolean allCorrect;
        for (var sheetAnswer : sheetAnswers) {
            //logger.debug("正在校验文字应用题答题卡明细TextExtension {} - {}", extension.getSheetDetailId(), extension.getContentHypertext());
            String sheetAnswerText = sheetAnswer.getContentText();
            Answer correctAnswer = problemBuilder.findAnswerByIndex(answers, sheetAnswer.getAnswerIndex());

            boolean isCorrect = false;
            if (Objects.isNull(correctAnswer)) {
                logger.error("Cannot find the correct answer by index. The answer index is {}.", sheetAnswer.getAnswerIndex());
            } else {
                isCorrect = AnswerValidator.validateAnswerByType(correctAnswer.getAnswerType(), sheetAnswerText, correctAnswer.getAnswerText());
                if (isCorrect) correctQty = correctQty + 1;
            }
            scoreSheetAnswers(sheetAnswer, correctAnswer, isCorrect);
        }
        var correctStatus = getCorrectStatus(answers.size(), correctQty);
        double totalScore = getQuestionTotalScore(question);
        double gainScore = totalScore * ((double) correctQty / answers.size());
        return reviewSheetDetail(detail, correctStatus, gainScore);
    }

    private double scoreSheetDetailWithAnswer(SheetDetail detail, Question question) {
        var correctAnswer = question.getCurrentAnswer();
        String correctAnswerText = correctAnswer.getAnswerText();
        String sheetAnswerText = detail.getFirstAnswer().getContentText();
        boolean isCorrect = AnswerValidator.validateAnswerByType(correctAnswer.getAnswerType(), sheetAnswerText, correctAnswerText);
        return reviewSheetDetail(detail, question, correctAnswer, isCorrect);
    }

    private double scoreUnitConversion(UnitConversionSheetDetail detail, UnitConversionQuestion question) {
        var sheetAnswers = detail.getSheetAnswers();
        var answers = question.getAnswers();
        int correctQty = 0;
        boolean allCorrect;
        for (var extension : sheetAnswers) {
            logger.debug("正在校验答题卡明细TextExtension {} - {}", extension.getSheetDetailId(), extension.getContentText());
            String sheetAnswerText = extension.getContentText();
            Answer correctAnswer = problemBuilder.findAnswerByIndex(answers, extension.getAnswerIndex());
            boolean isCorrect = false;
            if (Objects.isNull(correctAnswer)) {
                logger.error("Cannot find the correct answer by index. The answer index is {}.", extension.getAnswerIndex());
            } else {
                isCorrect = AnswerValidator.validateAnswerByType(correctAnswer.getAnswerType(), sheetAnswerText, correctAnswer.getAnswerText());
                if (isCorrect) correctQty = correctQty + 1;
            }
            scoreSheetAnswers(extension, correctAnswer, isCorrect);
        }
        var correctStatus = getCorrectStatus(answers.size(), correctQty);
        double totalScore = getQuestionTotalScore(question);
        double gainScore = totalScore * ((double) correctQty / answers.size());
        return reviewSheetDetail(detail, correctStatus, gainScore);
    }

    private double scoreGeometryCompletion(GeometryCompletionSheetDetail detail, GeometryCompletionQuestion question) {
        var sheetAnswers = detail.getSheetAnswers();
        var answers = question.getAnswers();
        int correctQty = 0;
        boolean allCorrect;
        for (var extension : sheetAnswers) {
            logger.debug("正在校验答题卡明细TextExtension {} - {}", extension.getSheetDetailId(), extension.getContentText());
            String sheetAnswerText = extension.getContentText();
            Answer correctAnswer = problemBuilder.findAnswerByIndex(answers, extension.getAnswerIndex());
            boolean isCorrect;
            if (Objects.isNull(correctAnswer)) {
                isCorrect = false;
            } else {
                isCorrect = AnswerValidator.validateAnswerByType(correctAnswer.getAnswerType(), sheetAnswerText, correctAnswer.getAnswerText());
                if (isCorrect) correctQty = correctQty + 1;
            }
            scoreSheetAnswers(extension, correctAnswer, isCorrect);
        }
        var correctStatus = getCorrectStatus(answers.size(), correctQty);
        double totalScore = getQuestionTotalScore(question);
        double gainScore = totalScore * ((double) correctQty / answers.size());
        return reviewSheetDetail(detail, correctStatus, gainScore);
    }

    private double scoreGeometryCalculation(GeometryCalculationSheetDetail detail, GeometryCalculationQuestion question) {
        var correctAnswer = question.getAnswer();
        String correctAnswerText = correctAnswer.getAnswerText();
        String sheetAnswerText = detail.getFirstAnswer().getContentText();
        boolean isCorrect = AnswerValidator.validateAnswerByType(correctAnswer.getAnswerType(), sheetAnswerText, correctAnswerText);
        return reviewSheetDetail(detail, question, correctAnswer, isCorrect);
    }

    private double scoreProbabilityCalculation(ProbabilityCalculationSheetDetail detail, ProbabilityCalculationQuestion question) {
        var correctAnswer = question.getAnswer();
        String correctAnswerText = correctAnswer.getAnswerText();
        String sheetAnswerText = detail.getFirstAnswer().getContentText();
        boolean isCorrect = AnswerValidator.validateAnswerByType(correctAnswer.getAnswerType(), sheetAnswerText, correctAnswerText);
        return reviewSheetDetail(detail, question, correctAnswer, isCorrect);
    }

    private double scoreStatisticsCalculation(StatisticsCalculationSheetDetail detail, StatisticsCalculationQuestion question) {
        var correctAnswer = question.getAnswer();
        String correctAnswerText = correctAnswer.getAnswerText();
        String sheetAnswerText = detail.getFirstAnswer().getContentText();
        boolean isCorrect = AnswerValidator.validateAnswerByType(correctAnswer.getAnswerType(), sheetAnswerText, correctAnswerText);
        return reviewSheetDetail(detail, question, correctAnswer, isCorrect);
    }

    private double scoreColumnCalculation(ColumnCalculationSheetDetail detail, ColumnCalculationQuestion question) {
        var correctAnswer = question.getAnswer();
        String correctAnswerText = correctAnswer.getAnswerText();
        String sheetAnswerText =  detail.getFirstAnswer().getContentText();
        boolean isCorrect = AnswerValidator.validateAnswerByType(correctAnswer.getAnswerType(), sheetAnswerText, correctAnswerText);
        return reviewSheetDetail(detail, question, correctAnswer, isCorrect);
    }

    private double scoreMultipleSelection(@NotNull MultipleSelectionSheetDetail detail, @NotNull MultipleSelectionQuestion question) {
        var correctAnswer = question.getAnswer();
        var correctAnswerText = correctAnswer.getAnswerText();
        String sheetAnswerText =  detail.getFirstAnswer().getOptionContentText();
        boolean isCorrect = AnswerValidator.validateMultipleOptionAnswer(sheetAnswerText, correctAnswerText);
        return reviewSheetDetail(detail, question, correctAnswer, isCorrect);
    }

    /**
     * 填空题评分
     *
     * @param detail
     * @param question
     * @return
     */
    private double scoreCompletion(@NotNull CompletionSheetDetail detail, @NotNull CompletionQuestion question) {
        var sheetAnswers = detail.getSheetAnswers();
        var answers = question.getAnswers();
        if (CollectionUtils.isEmpty(answers)) {
            logger.error("答题卡明细对应的问题答案为空。 答题卡明细编号: {} 问题编号:{}", detail.getDetailId(), question.getQuestionId());
        }
        int correctQty = 0;
        boolean allCorrect;
        for (var sheetAnswer : sheetAnswers) {
            String sheetAnswerText = sheetAnswer.getContentText();
            // logger.debug("Finding the correct answer: {}", sheetAnswerText);
            Answer correctAnswer = problemBuilder.findAnswerByIndex(answers, sheetAnswer.getAnswerIndex());
            boolean isCorrect = false;
            if (Objects.isNull(correctAnswer)) {
                logger.warn("Cannot find the correct answer. The answer index is {}", sheetAnswer.getAnswerIndex());
            } else {
                isCorrect = AnswerValidator.validateAnswerByType(correctAnswer.getAnswerType(), sheetAnswerText, correctAnswer.getAnswerText());
                if (isCorrect) correctQty = correctQty + 1;
            }
            scoreSheetAnswers(sheetAnswer, correctAnswer, isCorrect);
        }
        var correctStatus = getCorrectStatus(answers.size(), correctQty);
        double totalScore = getQuestionTotalScore(question);
        double gainScore = totalScore * ((double) correctQty / answers.size());
        return reviewSheetDetail(detail, correctStatus, gainScore);
    }

    /**
     * 计算问题评分
     *
     * @param detail
     * @param question
     * @return
     */
    private double scoreCalculation(@NotNull CalculationSheetDetail detail, @NotNull CalculationQuestion question) {
        var correctAnswer = question.getAnswer();
        String correctAnswerText = correctAnswer.getAnswerText();
        String sheetAnswerText = detail.getFirstAnswer().getContentText();
        boolean isCorrect = AnswerValidator.validateAnswerByType(correctAnswer.getAnswerType(), sheetAnswerText, correctAnswerText);
        return reviewSheetDetail(detail, question, correctAnswer, isCorrect);
    }

    /**
     * 单选问题评分
     *
     * @param detail
     * @param question
     * @return
     */
    private double scoreMultipleChoice(@NotNull MultipleChoiceSheetDetail detail, @NotNull MultipleChoiceQuestion question) {
        var correctAnswer = question.getAnswer();
        long correctAnswerOptionId = correctAnswer.getValue();
        var sheetAnswerOptionText = detail.getFirstAnswer().getOptionContentText();

        long sheetAnswerOptionId = ParseUtils.parseLong(sheetAnswerOptionText);
        boolean isCorrect = AnswerValidator.validateOption(sheetAnswerOptionId, correctAnswerOptionId);
        return reviewSheetDetail(detail, question, correctAnswer, isCorrect);
    }

    /**
     * 对TextExtension明细进行评分
     *
     * @param sheetAnswer
     * @param correctAnswer
     * @param isCorrect
     */
    private void scoreSheetAnswers(SheetAnswer sheetAnswer, Answer correctAnswer, boolean isCorrect) {
        if (Objects.nonNull(correctAnswer)) {
            //textExtension.setAnswerType(correctAnswer.getAnswerType());
            //textExtension.setAnswer(correctAnswer);
        }
        sheetAnswer.setCorrect(isCorrect);
        sheetRepository.saveSheetAnswer(sheetAnswer);
    }

    /**
     * 判断问题评分
     *
     * @param detail
     * @param question
     * @return
     */
    private double scoreTrueFalse(@NotNull TrueFalseSheetDetail detail, @NotNull TrueFalseQuestion question) {
        var correctAnswer = question.getAnswer();
        var correctAnswerText = correctAnswer.getAnswerText();
        var sheetAnswerText = detail.getFirstAnswer().getContentText();
        boolean isCorrect = AnswerValidator.validateBooleanAnswer(sheetAnswerText, correctAnswerText);
        return reviewSheetDetail(detail, question, correctAnswer, isCorrect);
    }

    // endregion


    /**
     * 评分后更新答题卡明细
     *
     * @param detail
     * @param question
     * @param correctAnswer
     * @param isCorrect
     * @return
     */
    private double reviewSheetDetail(@NotNull SheetDetail detail, @NotNull Question question, @NotNull Answer correctAnswer, boolean isCorrect) {
        Problem problem = problemRepository.getProblem(question.getProblemId());
        if (Objects.isNull(problem)) {
            logger.debug("Cannot find the problem. The problem id is {}.", question.getProblemId());
            return 0;
        }

        var allSkipped = true;
        for (var sheetAnswer : detail.getSheetAnswers()) {
            if (!sheetAnswer.isSkipped())
                allSkipped = false;
        }

        double score = calculatesQuestionScore(problem, question, allSkipped, isCorrect);
        detail.setReviewed(true);
        detail.setReviewCount(detail.getReviewCount() + 1);
        int seconds = detail.getAnsweringSeconds();
        detail.setAnsweringSeconds(seconds);
        detail.setScored(true);
        detail.setGainScore(score);
        detail.setProblemId(problem.getProblemId());
        sheetRepository.saveSheetDetail(detail);
        return score;
    }

    /**
     * 批改后更新答题卡明细
     *
     * @param detail
     * @param isCorrect
     * @param score
     * @return
     */
    private double reviewSheetDetail(@NotNull SheetDetail detail, int correctStatus, double score) {
        detail.setReviewed(true);
        detail.setReviewCount(detail.getReviewCount() + 1);
        int seconds = detail.getAnsweringSeconds();
        detail.setAnsweringSeconds(seconds);
        detail.setScored(true);
        detail.setGainScore(score);
        detail.setCorrectStatus(correctStatus);
        sheetRepository.saveSheetDetail(detail);
        return score;
    }


    // region question score
    /**
     * 获取一个问题总分 （可能存在多答案）
     *
     * @param question
     * @return
     */
    private double getQuestionTotalScore(@NotNull Question question) {
        Problem problem = problemRepository.getProblem(question.getProblemId());
        return getQuestionTotalScore(problem, question);
    }

    private double getQuestionTotalScore(@NotNull Problem problem, @NotNull Question question) {
        long examId = problemCollectionRepository.getExamIdByCollection(problem.getCollectionId());
        long problemTypeId = problem.getProblemType().getTypeId();
        long questionTypeId = question.getQuestionType().getTypeId();
        ScoringRule rule = examRepository.getScoringRule(examId, problemTypeId, questionTypeId);
        if (Objects.nonNull(rule)) {
            return rule.getCorrectScore();
        } else {
            // 为简化积分规则，暂不考虑忽略题的分数
            return question.getQuestionScore();
        }
    }

    /**
     * 批改根据计分规则计算得分
     * 首先根据 examId、problemTypeId、questionTypeId获取ScoringRule
     * 如果ScoringRule规则存在，则以规则计分：正确获得correctScore分数，错误获得incorrectScore分数， 未答题获得skipScore分数
     * 如果ScoringRule规则不存在，则以Question中questionScore为标准，正确答题得分，错误及未答题不得分
     *
     * @param question
     * @param isSkipped
     * @param isCorrect
     * @return
     */
    private double calculatesQuestionScore(@NotNull Problem problem, @NotNull Question question, boolean isSkipped, boolean isCorrect) {
        var collection = problemCollectionRepository.getProblemCollection(problem.getCollectionId());
        long examId = 0;
        if (Objects.nonNull(collection)) examId = collection.getExamId();
        long problemTypeId = problem.getProblemType().getTypeId();
        long questionTypeId = question.getQuestionType().getTypeId();
        ScoringRule rule = examRepository.getScoringRule(examId, problemTypeId, questionTypeId);
        if (Objects.nonNull(rule)) {
            if (isSkipped) {
                return rule.getSkipScore();
            } else if (isCorrect) {
                return rule.getCorrectScore();
            } else return rule.getIncorrectScore();
        } else {
            // 为简化积分规则，暂不考虑忽略题的分数
            double score = question.getQuestionScore();
            if (isCorrect)
                return score;
        }
        return 0.0;
    }

    // endregion

    @HandleServiceException
    public int getSheetQuantityByMonth() {
        LocalDate now = LocalDate.now();
        LocalDate dateStart = DateTimeHelper.getFirstDayOfMonth(now.getYear(), now.getMonthValue());
        LocalDate dateEnd = DateTimeHelper.getLastDayOfMonth(now.getYear(), now.getMonthValue());
        var dateTimeStart = dateStart.atTime(0,0,0);
        var dateTimeEnd = dateEnd.plusDays(1).atTime(0,0,0);
        return sheetRepository.getSheetsQuantityByDate(dateTimeStart, dateTimeEnd);
    }
}
